1 module hip.util.to_string_range;
2 import std.range.primitives;
3 import hip.util.reflection:isArray;
4 
5 void put(Sink, E)(ref Sink sink, E e)
6 {
7     static if(is(E == U[], U))
8     {
9         static if(__traits(hasMember, sink, "preAllocate"))
10             sink.preAllocate(e.length);
11         foreach(element; e)
12             sink.put(element);
13     }
14     else
15         sink.put(e);
16 }
17 
18 void put(ref char[] sink, char ch)
19 {
20     sink[0] = ch;
21     sink = sink[1..$];
22 }
23 
24 void put(ref char[] sink, string str)
25 {
26     sink[0..str.length] = str;
27     sink = sink[str.length..$];
28 }
29 
30 
31 void toStringRange(Sink, Enum)(ref Sink sink, Enum enumMember) if(is(Enum == enum))
32 {
33     foreach(mem; __traits(allMembers, Enum))
34         if(__traits(getMember, Enum, mem) == enumMember)
35         {
36             put(sink, Enum.stringof ~ "." ~ mem); //@nogc string, resolved at compile time
37             return;
38         }
39     put(sink, Enum.stringof ~ ".|MEMBER_NOT_FOUND|"); //@nogc string, resolved at compile time
40 }
41 
42 void toStringRange(Sink)(ref Sink sink, float f)
43 if(isOutputRange!(Sink, char))
44 {
45     if(f != f) //nan
46         return put(sink, "nan");
47     else if(f == -float.infinity)
48         return put(sink, "-inf");
49     if(f == float.infinity)
50         return put(sink, "inf");
51     if(f < 0)
52     {
53         f = -f;
54         put(sink, '-');
55     }
56     
57     float decimal = f - cast(int)f;
58     toStringRange(sink, cast(int)f);
59     if(decimal == 0)
60         return;
61     put(sink, '.');
62     long multiplier = 10;
63     while(cast(long)(decimal*multiplier) < (decimal*multiplier))
64     {
65         if(cast(long)(decimal*multiplier) == 0)
66             put(sink, '0');
67         multiplier*=10;
68     }
69     toStringRange(sink, (cast(long)(decimal*multiplier)));
70 }
71 
72 
73 void toStringRange(Sink, T)(ref Sink sink, T[] arr)
74 if(isOutputRange!(Sink, char) && !is(T[] == string) && !is(T[] == char[])) //There is a better match for char/string
75 {
76     static if(__traits(compiles, sink.preAllocate))
77     {
78         //2: '[' and ']'
79         // 2 * arr.length: ", " (no need to use - 1 as there will be the inputs)
80         sink.preAllocate(2 + 2 * arr.length);
81     }
82     put(sink, '[');
83     for(int i = 0; i < arr.length; i++)
84     {
85         if(i != 0)
86         {
87             foreach(character; ", ")
88                 put(sink, character);
89         }
90         toStringRange(sink, arr[i]);
91     }
92     put(sink, ']');
93 }
94 
95 
96 void toStringRange(Sink, T)(ref Sink sink, T structOrTupleOrClass)
97 if(!isArray!T && (is(T == struct) || is(T == class) || is(T == interface)))
98 {
99     static if(is(T == struct))//For structs declaration
100     {
101         import hip.util.reflection;
102         alias struct_ = structOrTupleOrClass;
103         static if(__traits(hasMember, T, "toString") && hasUDA!(__traits(getMember, T, "toString"), "format"))
104         {
105             import hip.util.format;
106             formatFromType(sink, struct_);
107         }
108         else
109         {
110             put(sink, T.stringof);
111             put(sink, '(');
112             foreach(i, v; struct_.tupleof)
113             {
114                 if(i > 0)
115                     put(sink, ", ");
116                 toStringRange(sink, v);
117             }
118             put(sink, ')');
119         }
120     }
121     else static if(is(T == class))
122     {
123         alias class_ = structOrTupleOrClass;
124         put(sink, T.classinfo.name);
125         put(sink, '(');
126         foreach(i, v; class_.tupleof)
127         {
128             if(i > 0)
129                 put(sink, ", ");
130             toStringRange(sink, v);
131         }
132         put(sink, ')');
133     }
134     else static if(is(T == interface))
135     {
136         put(sink, T.classinfo.name);
137     }
138     // else static if(isTuple!T)
139     // {
140     //     alias tupl = structOrTupleOrClass;
141     //     put(sink, T.stringof);
142     //     put(sink, '(');
143     //     foreach(i, v; tupl)
144     //     {
145     //         if(i > 0)
146     //             put(sink, ", ");
147     //         toStringRange(sink, v);
148     //     }
149     //     put(sink, ')');
150     // }
151     else static assert(0, "Not implemented for "~T.stringof);
152 }
153 
154 // void toStringRange(Sink)(auto ref Sink sink, scope const char[] arr) if(isOutputRange!(Sink, char))
155 // {
156 //     static if(__traits(compiles, sink.preAllocate))
157 //         sink.preAllocate(arr.length);
158 //     foreach(ch; arr)
159 //         put(sink, ch);
160 // }
161 
162 void   toStringRange(Sink)(ref Sink sink, string str) if(isOutputRange!(Sink, char))
163 {
164     static if(__traits(compiles, sink.preAllocate))
165         sink.preAllocate(str.length);
166     foreach(character; str)
167         put(sink, character);
168 }
169 
170 void   toStringRange(Sink)(ref Sink sink, const(char)* str) if(isOutputRange!(Sink, char))
171 {
172     import core.stdc.string:strlen;
173     size_t length = strlen(str);
174     static if(__traits(compiles, sink.preAllocate))
175         sink.preAllocate(length);
176     for(int i = 0; i < length; i++)
177         put(sink, str[i]);
178 }
179 
180 void   toStringRange(Sink)(ref Sink sink, const(ubyte)* str) if(isOutputRange!(Sink, char))
181 {
182     toStringRange(sink, cast(const(char)*)str);
183 }
184 
185 void toStringRange(Sink)(ref Sink sink, void* ptr) if(isOutputRange!(Sink, char))
186 {
187     if(ptr is null)
188         put(sink, "null");
189     else
190         toHex(sink, cast(size_t)ptr);
191 }
192 
193 void toStringRange(Sink)(ref Sink sink, bool b) if(isOutputRange!(Sink, char))
194 {
195     put(sink, b ? "true" :"false");
196 }
197 
198 void toStringRange(Sink)(ref Sink sink, long x)
199 if(isOutputRange!(Sink, char))
200 {
201     enum numbers = "0123456789";
202     int preAllocSize = 0;
203     bool isNegative = x < 0;
204     if(isNegative)
205     {
206         x*= -1;
207         preAllocSize++;
208     }
209     ulong div = 10;
210     while(div <= x)
211     {
212         div*=10;
213         preAllocSize++;
214     }
215     div/= 10;
216     static if(__traits(hasMember, sink, "preAllocate"))
217         sink.preAllocate(preAllocSize);
218     if(isNegative)
219         put(sink, '-');
220     while(div >= 10)
221     {
222         put(sink, numbers[(x/div)%10]);
223         div/=10;
224     }
225     put(sink, numbers[cast(size_t)(x%10)]);
226 }
227 
228 
229 void toHex(Sink)(ref Sink sink, size_t n)
230 if(isOutputRange!(Sink, char))
231 {
232     enum numbers = "0123456789ABCDEF";
233     int preAllocSize = 1;
234     ulong div = 16;
235     while(div <= n)
236     {
237         div*= 16;
238         preAllocSize++;
239     }
240     div/= 16;
241     static if(__traits(hasMember, sink, "preAllocate"))
242         sink.preAllocate(preAllocSize);
243 
244     while(div >= 16)
245     {
246         put(sink, numbers[(n/div)%16]);
247         div/= 16;
248     }
249     put(sink, numbers[n%16]);
250 }